<template>
  <div>
    <div v-if="!$slots.default" class="count-down" v-html="formattedTime" />
    <slot v-else :time-data="timeData" />
  </div>
</template>

<script lang="ts" setup>
import type { TimeDataModel } from './utils'
import { formatTime } from './utils'
const props = withDefaults(
  defineProps<{
    time: number | string
    millisecond?: boolean
    hideText?: boolean
    autoStart?: boolean
    textWidth?: number
  }>(),
  {
    autoStart: true,
    hideText: false,
    textWidth: 40
  }
)

const emit = defineEmits<{
  (e: 'change', val: number): void
  (e: 'finish'): void
}>()

const interval = props.millisecond ? 16 : 1000

const { time, autoStart } = toRefs(props)

let leftTime = +time.value * 1000
let ticker: any = null

const timeData = reactive<TimeDataModel>(formatTime(leftTime))

const updateTime = (timeData: number, leftTime: number) => {
  const data = formatTime(leftTime)
  Object.keys(timeData).forEach((k: any) => {
    timeData[k] = data[k]
  })
}

const start = () => {
  if (!ticker && leftTime > 0) {
    // TODO 内存泄漏可疑点: 确保客户端执行并及时清除
    ticker = setInterval(() => {
      leftTime -= interval
      if (leftTime <= 0) {
        leftTime = 0
        clearInterval(ticker)
        emit('finish')
      } else {
        emit('change', leftTime)
      }

      updateTime(timeData, leftTime)
    }, interval)
  }
}

const stop = () => {
  clearInterval(ticker)
  ticker = null
}

const restart = () => {
  clearInterval(ticker)
  ticker = null
  leftTime = props.time

  emit('change', leftTime)
  updateTime(timeData, leftTime)

  start()
}

onMounted(() => {
  // autoStart.value && start()
})

onBeforeUnmount(() => {
  stop()
})

const formattedTime = computed(() => {
  const units = ['hours', 'minutes', 'seconds', 'milliseconds']
  const unitsText = ['时', '分', '秒', '毫秒']

  const day = timeData.days
  if (day !== 0 && day !== '00') {
    units.unshift('days')
    unitsText.unshift('天')
  }

  const timeStrings = units
    .filter((unit) => unit !== 'milliseconds' || props.millisecond)
    .map((unit, index) => {
      const value = timeData[unit]
      const unitString = props.hideText ? '' : unitsText[index]
      return `<span style='display:inline-block;text-align:center;width:${props.textWidth}px' >${value}</span>${unitString}`
    })
  return props.hideText ? timeStrings.join(':') : timeStrings.join('')
})

defineExpose({ restart })
</script>

<style lang="scss" scoped>
.count-down {
  color: #eb2330;
  font-size: 14px;
  font-weight: 400;
  line-height: 14px;
  :deep() {
    .time-item {
      display: inline-block;
      width: 20px;
      text-align: center;
    }
  }
}
</style>
